package com.cjl.eater.service.impl;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cjl.common.constants.*;
import com.cjl.common.dtos.LoginDto;
import com.cjl.common.dtos.PageDto;
import com.cjl.common.dtos.ResponseResult;
import com.cjl.common.enums.HttpEnums;
import com.cjl.eater.dtos.EaterFoodRelationsDto;
import com.cjl.eater.dtos.EaterInfoDto;
import com.cjl.eater.mapper.EaterFoodRelationsMapper;
import com.cjl.eater.mapper.EaterMapper;
import com.cjl.eater.pojos.Eater;
import com.cjl.eater.pojos.EaterFoodRelations;
import com.cjl.eater.service.EaterService;
import com.cjl.feign.food.FoodClient;
import com.cjl.utils.common.JwtUtil;
import com.cjl.utils.common.RandomStrUtil;
import com.cjl.utils.thread.EaterThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.*;

/**
 * @author cjl
 * @version v1.0
 * @description TODO
 * @createTime 2023/2/7 17:02
 */
@Service
@Slf4j
@Transactional
public class EaterServiceImpl extends ServiceImpl<EaterMapper, Eater> implements EaterService {

    /**
     * 登录/注册, 0-注册 1-登录
     *
     * @param eaterLoginDto 食客登录dto
     * @return ResponseResult
     */
    @Override
    public ResponseResult login(LoginDto eaterLoginDto) {
        //参数校验
        if (checkParams(eaterLoginDto)) return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID);
        //根据手机号获取用户
        String phone = eaterLoginDto.getPhone();
        Eater dbEater = getEaterByPhone(phone);

        //判断是注册还是登录
        Short status = eaterLoginDto.getStatus();
        if (status == LoginStatusConstants.REGISTER) {
            //注册
            if (dbEater != null) {
                //手机号对应的用户已存在，则无法注册
                return ResponseResult.errorResult(HttpEnums.EATER_ALREADY_EXIST);

            }
            //随机6位字符串,加密
            String salt = RandomStrUtil.randomStr();
            String password = DigestUtils.md5DigestAsHex(
                    (eaterLoginDto.getPassword() + salt).getBytes()
            );


            //注册用户，保存数据
            Eater newEater = new Eater();
            BeanUtils.copyProperties(eaterLoginDto, newEater);
            newEater.setCreatedTime(new Date());
            newEater.setPassword(password);
            newEater.setSalt(salt);

            boolean result;
            try {
                log.info("添加用户{}到数据库中...", newEater);
                result = save(newEater);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("用户没有添加到数据库");
            }
            if (!result) {
                return ResponseResult.errorResult(HttpEnums.DB_UPDATE_DATA_ERROR);
            }
            log.info("用户{}成功添加到数据库", newEater);
            //屏蔽敏感信息
            wipeSensitiveMessage(newEater);
            return ResponseResult.okResult(newEater);
            //=============================================
        } else if (status == LoginStatusConstants.LOGIN) {
            //登录
            if (dbEater == null) {
                //手机号对应的用户不存在，需要注册
                return ResponseResult.errorResult(HttpEnums.EATER_NOT_EXIST);
            }
            String password = DigestUtils.md5DigestAsHex((eaterLoginDto.getPassword() + dbEater.getSalt()).getBytes());
            if (!password.equals(dbEater.getPassword())) return ResponseResult.errorResult(HttpEnums.DB_PASSWORD_ERROR);

            //生成token
            Map<String, Object> map = new HashMap<>();
            String token = JwtUtil.getToken(dbEater.getId().longValue());
            map.put("token", token);
            //屏蔽敏感信息
            wipeSensitiveMessage(dbEater);
            map.put("dbEater", dbEater);
            return ResponseResult.okResult(map);
        } else {
            //非法参数
            return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID);
        }
    }

    /**
     * 屏蔽敏感信息
     *
     * @param eater
     */
    private void wipeSensitiveMessage(Eater eater) {
        eater.setSalt(null);
        eater.setId(null);
        eater.setPassword(null);
    }


    /**
     * 参数校验：登录，注册，修改密码
     *
     * @param eaterLoginDto
     * @return boolean
     */
    private boolean checkParams(LoginDto eaterLoginDto) {
        //参数校验
        return eaterLoginDto == null
                || StringUtils.isBlank(eaterLoginDto.getPhone())
                || StringUtils.isBlank(eaterLoginDto.getPassword())
                || eaterLoginDto.getStatus() == null;
    }

    /**
     * 修改密码
     *
     * @param eaterLoginDto
     * @return ResponseResult
     */
    @Override
    public ResponseResult modifyPassword(LoginDto eaterLoginDto) {
        //参数校验
        if (checkParams(eaterLoginDto)) return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID);
        //判断是否是修改密码
        if (eaterLoginDto.getStatus() != LoginStatusConstants.MODIFY) {
            return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID);
        }
        //根据手机号获取用户
        Eater dbEater = getOne(new LambdaQueryWrapper<Eater>().eq(Eater::getPhone, eaterLoginDto.getPhone()));
        if (dbEater == null) {
            return ResponseResult.errorResult(HttpEnums.EATER_NOT_EXIST);
        }
        //修改密码，更新数据
        dbEater.setPassword(DigestUtils.md5DigestAsHex((eaterLoginDto.getPassword() + dbEater.getSalt()).getBytes()));
        dbEater.setUpdatedTime(new Date());
        boolean result;
        try {
            result = update(dbEater, new LambdaQueryWrapper<Eater>().eq(Eater::getPhone, eaterLoginDto.getPhone()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("数据库更新密码异常");
        }

        if (result) {
            //屏蔽敏感信息
            wipeSensitiveMessage(dbEater);
            return ResponseResult.okResult(dbEater);
        } else {
            return ResponseResult.errorResult(HttpEnums.DB_UPDATE_DATA_ERROR);
        }
    }

    /**
     * 填写个人信息
     * todo=================
     *
     * @param eaterInfoDto
     * @return ResponseResult
     */
    @Override
    public ResponseResult modifyEaterInfo(EaterInfoDto eaterInfoDto) {
        //参数校验
        if (eaterInfoDto == null) {
            return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID, "缺少参数");
        }

        //从ThreadLocal中获取食客id
        Integer eaterId = EaterThreadUtil.get().getId();

        //根据id获取食客
        Eater oldEater = getEaterById(eaterId);
        if (oldEater == null) {
            //用户不存在
            return ResponseResult.errorResult(HttpEnums.EATER_NOT_EXIST);
        }
        //设置昵称
        if (StringUtils.isNotBlank(eaterInfoDto.getName())) oldEater.setName(eaterInfoDto.getName());
        //设置出生日期
        try {
            if (eaterInfoDto.getBirthday() != null) oldEater.setBirthday(eaterInfoDto.getBirthday());
        } catch (Exception e) {
            log.error("出生日期格式非法");
            e.printStackTrace();
            return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID, "出生日期非法");
        }

        Short sex = eaterInfoDto.getSex();
        //设置性别
        if (sex != null) {
            if (sex.equals(SexConstants.SEX_WOMAN) || sex.equals(SexConstants.SEX_MAN) || sex.equals(SexConstants.SEX_UNKNOWN)) {
                oldEater.setSex(sex);
            } else {
                return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID, "性别无效");
            }
        }

        //最近一次修改时间
        oldEater.setUpdatedTime(new Date());

        //更新用户信息
        boolean result = updateById(oldEater);
        if (result) {
            oldEater.setId(null);
            oldEater.setPassword(null);
            //屏蔽敏感信息
            wipeSensitiveMessage(oldEater);
            return ResponseResult.okResult(oldEater);
        } else {
            return ResponseResult.errorResult(HttpEnums.DB_UPDATE_DATA_ERROR);
        }

    }

    @Autowired
    EaterFoodRelationsMapper eaterFoodRelationsMapper;

    @Autowired
    FoodClient foodClient;

    /**
     * 保存食客行为记录
     * 包括点赞和收藏
     *
     * @param eaterFoodRelationsDto
     * @return ResponseResult
     */
    @Override
    public ResponseResult saveEaterBehavior(EaterFoodRelationsDto eaterFoodRelationsDto) {

        //根据id获取用户
        Integer eaterId;
        try {
            eaterId = EaterThreadUtil.get().getId();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.errorResult(HttpEnums.UN_AUTHORIZE);
        }
        Eater dbEater = getEaterById(eaterId);

        //判断食客是否存在
        if (dbEater == null) return ResponseResult.errorResult(HttpEnums.EATER_NOT_EXIST);
        //检查参数
        if (eaterFoodRelationsDto == null || eaterFoodRelationsDto.getFoodId() == null)
            return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID);

        //获取参数
        Integer foodId = eaterFoodRelationsDto.getFoodId();
        Integer love = eaterFoodRelationsDto.getLove();
        Integer collect = eaterFoodRelationsDto.getCollect();

        //查询food是否存在
        ResponseResult responseResult = foodClient.getFoodById(foodId);
        if (!responseResult.getCode().equals(HttpEnums.SUCCESS.getCode()))
            return ResponseResult.errorResult(HttpEnums.FOOD_NOT_EXIST);

        //根据食客id和菜品id获取食客-菜品关系记录
        EaterFoodRelations dbEaterFoodRelations = eaterFoodRelationsMapper.selectOne(
                new LambdaQueryWrapper<EaterFoodRelations>()
                        .eq(EaterFoodRelations::getEaterId, eaterId)
                        .eq(EaterFoodRelations::getFoodId, foodId));


        int result;
        EaterFoodRelations newEaterFoodRelations;
        if (dbEaterFoodRelations == null) {
            //插入
            boolean loveFlag = true;
            boolean collectFlag = true;
            //如果食客第一次访问没有点赞或者收藏，则默认为取消点赞和取消收藏
            if (love == null || love != EaterFoodRelationsConstants.EATER_LOVE) {
                love = EaterFoodRelationsConstants.EATER_NOT_LOVE;
                loveFlag = false;
            }
            if (collect == null || collect != EaterFoodRelationsConstants.EATER_COLLECT) {
                collect = EaterFoodRelationsConstants.EATER_NOT_COLLECT;
                collectFlag = false;
            }
            newEaterFoodRelations = EaterFoodRelations.builder()
                    .eaterId(dbEater.getId())
                    .foodId(foodId)
                    .love(love)
                    .collect(collect)
                    .updatedTime(new Date())
                    .build();
            result = eaterFoodRelationsMapper.insert(newEaterFoodRelations);
            //保存收藏数量和点赞数量到redis中
            //foodId对应的food的点赞数量
            saveFoodLoveOrCollectCountToRedis(foodId, loveFlag, FoodRedisCountConstants.LOVE_COUNT, EaterFoodRelationsConstants.EATER_OPERATED_PESSIMISTIC);

            //foodId对应的food的收藏数量
            saveFoodLoveOrCollectCountToRedis(foodId, collectFlag, FoodRedisCountConstants.COLLECT_COUNT, EaterFoodRelationsConstants.EATER_OPERATED_PESSIMISTIC);

        } else {
            //更新
            Boolean loveFlag = null;
            Boolean collectFlag = null;
            Integer dbLove = dbEaterFoodRelations.getLove();
            Integer dbCollect = dbEaterFoodRelations.getCollect();
            if (love != null) {
                if (love == EaterFoodRelationsConstants.EATER_LOVE) loveFlag = true;
                else if (love == EaterFoodRelationsConstants.EATER_NOT_LOVE) loveFlag = false;
                else return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID, "参数点赞有误");
            } else {
                love = dbLove;
            }
            if (collect != null) {
                if (collect == EaterFoodRelationsConstants.EATER_COLLECT) collectFlag = true;
                else if (collect == EaterFoodRelationsConstants.EATER_NOT_COLLECT) collectFlag = false;
                else return ResponseResult.errorResult(HttpEnums.PARAMS_INVALID, "参数收藏有误");
            } else {
                collect = dbCollect;
            }
            newEaterFoodRelations = EaterFoodRelations.builder()
                    .eaterId(dbEater.getId())
                    .foodId(foodId)
                    .collect(collect)
                    .love(love)
                    .updatedTime(new Date())
                    .id(dbEaterFoodRelations.getId())
                    .build();
            result = eaterFoodRelationsMapper.updateById(newEaterFoodRelations);
            //保存收藏数量和点赞数量到redis中
            //foodId对应的food的点赞数量
            if (loveFlag != null)
                saveFoodLoveOrCollectCountToRedis(foodId, loveFlag, FoodRedisCountConstants.LOVE_COUNT, dbLove);
            //foodId对应的food的收藏数量
            if (collectFlag != null)
                saveFoodLoveOrCollectCountToRedis(foodId, collectFlag, FoodRedisCountConstants.COLLECT_COUNT, dbCollect);

        }
        //保存或更新数据失败
        if (result == 0) {
            return ResponseResult.errorResult(HttpEnums.DB_UPDATE_DATA_ERROR);
        }

        return ResponseResult.okResult(newEaterFoodRelations);
    }

    /**
     * 保存food的收藏数量或者点赞数量到redis中
     *
     * @param foodId    菜品id
     * @param flag      标记
     * @param countType 数量种类
     * @return
     */
    private void saveFoodLoveOrCollectCountToRedis(Integer foodId, boolean flag, String countType, Integer operatedType) {
        Object loveCount = redisTemplate.opsForHash().get(foodId.toString(), countType);
        //该food还没有被点赞(或收藏)过,第一次被这个人点赞
        if (loveCount == null && flag) {
            redisTemplate.opsForHash().put(foodId.toString(), countType, FoodRedisCountConstants.FIRST_OPERATED);
        }
        //该food还没有被点赞过,这次也没被这个人点赞
        else if (loveCount == null) {
            redisTemplate.opsForHash().put(foodId.toString(), countType, FoodRedisCountConstants.INIT_COUNT);
        }
        //该food之前被点赞过,这次也被这个人点赞了
        else if (flag) {
            //之前包括这个人也点赞过
            if (operatedType == EaterFoodRelationsConstants.EATER_OPERATED_OPTIMISTIC)
                return;
            //之前这个人没点赞过
            redisTemplate.opsForHash().increment(foodId.toString(), countType, FoodRedisCountConstants.INCREMENT_BY_ONE);

        }
        //该food之前被点赞过,这次没被点赞了
        else {
            //之前这个人没点赞过
            if (operatedType == EaterFoodRelationsConstants.EATER_OPERATED_PESSIMISTIC)
                return;
            //之前这个人点赞过
            redisTemplate.opsForHash().increment(foodId.toString(), countType, -FoodRedisCountConstants.INCREMENT_BY_ONE);

        }
    }

    @Autowired
    RedisTemplate<String, Integer> redisTemplate;

    /**
     * 根据菜名查询菜品
     *
     * @param foodName 菜名
     * @return ResponseResult
     */
    @Override
    public ResponseResult searchFood(String foodName) throws Exception {
        //获取eater
        Eater dbEater = getEaterById(EaterThreadUtil.get().getId());

        //检查用户身份有效性
        if (dbEater == null) return ResponseResult.errorResult(HttpEnums.UN_AUTHORIZE, "用户不存在，非法操作");

        log.info("用户{}查询菜品中。。。", dbEater.getName());
        ResponseResult responseResult = foodClient.searchFood(foodName);

        //判断是否查询成功
        if (!responseResult.getCode().equals(HttpEnums.SUCCESS.getCode())) return responseResult;

        Map data = (Map) responseResult.getData();

        if (data.get("foods") == null) {
            //精确查询，一个菜品
            EaterFoodRelations eaterFoodRelations;
            //根据map集合和食客获取食客-菜品关系记录
            eaterFoodRelations = getEaterFoodRelationsByMapAndEaterFromDB(data, dbEater);

            //重置通用状态结果返回的data
            Map<String, Object> map = resetResponseResultData(dbEater, data, eaterFoodRelations);
            return ResponseResult.okResult(map);
        } else {
            //模糊查询，查询到的是List集合
            List list = (List) data.get("foods");
            ListIterator listIterator = list.listIterator();
            List<Map> newList = new LinkedList<>();
            //遍历集合，依次获取元素，重新加入用户行为记录再返回给前端
            while (listIterator.hasNext()) {
                Map next = (Map) listIterator.next();

                EaterFoodRelations eaterFoodRelations;
                //根据map集合和食客获取食客-菜品关系记录
                eaterFoodRelations = getEaterFoodRelationsByMapAndEaterFromDB(next, dbEater);
                //重置通用状态结果返回的data
                Map<String, Object> map = resetResponseResultData(dbEater, next, eaterFoodRelations);
                newList.add(map);
            }
            return ResponseResult.okResult(newList);
        }
    }

    /**
     * 重置通用状态结果返回的data
     *
     * @param dbEater            食客
     * @param map                旧的通用状态结果返回类重的data
     * @param eaterFoodRelations 食客菜品关系记录
     * @return Map<String, Object>
     */
    private Map<String, Object> resetResponseResultData(Eater dbEater, Map map, EaterFoodRelations eaterFoodRelations) throws Exception {
        //如果为空，则保存默认值（取消点赞+取消收藏）
        if (eaterFoodRelations == null) {
            EaterFoodRelationsDto eaterFoodRelationsDto = EaterFoodRelationsDto.builder()
                    .foodId((Integer) map.get("id"))
                    .love(EaterFoodRelationsConstants.EATER_NOT_LOVE)
                    .collect(EaterFoodRelationsConstants.EATER_NOT_COLLECT)
                    .build();
            //保存食客行为记录
            saveEaterBehavior(eaterFoodRelationsDto);

            //获取食客行为记录
            eaterFoodRelations = getEaterFoodRelationsByMapAndEaterFromDB(map, dbEater);

        }
        Map<String, Object> newMap = new HashMap<>();
        newMap.put("food", map);
        newMap.put("eaterFoodRelations", eaterFoodRelations);
        return newMap;
    }


    /**
     * 根据map集合和食客获取食客-菜品关系记录
     *
     * @param map   里面需要封装菜品id
     * @param eater 食客，需要封装食客id
     * @return EaterFoodRelations
     */
    private EaterFoodRelations getEaterFoodRelationsByMapAndEaterFromDB(Map map, Eater eater) {
        Integer foodId = (Integer) map.get("id");
        return eaterFoodRelationsMapper.selectOne(new LambdaQueryWrapper<EaterFoodRelations>()
                .eq(EaterFoodRelations::getFoodId, foodId)
                .eq(EaterFoodRelations::getEaterId, eater.getId()));
    }

    /**
     * 根据id获取食客
     *
     * @param id id
     * @return Eater
     */
    private Eater getEaterById(Integer id) {
        return getById(id);
    }

    /**
     * 菜品列表
     *
     * @param pageDto 分页dto
     * @return ResponseResult
     */
    @Override
    public ResponseResult foodList(PageDto pageDto) {
        //获取eater
        Eater dbEater = getEaterById(EaterThreadUtil.get().getId());

        //检查用户身份有效性
        if (dbEater == null) return ResponseResult.errorResult(HttpEnums.UN_AUTHORIZE, "用户不存在，非法操作");

        log.info("用户{}查询菜品列表中。。。", dbEater.getName());
        return foodClient.foodList(pageDto);
    }

    /**
     * 根据手机号获取用户
     *
     * @param phone
     * @return Eater
     */
    private Eater getEaterByPhone(String phone) {
        LambdaQueryWrapper<Eater> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Eater::getPhone, phone);
        return getOne(lambdaQueryWrapper);
    }
}
